﻿/*
 *	Copyright (c) 2009 Queensland University of Technology. All rights reserved.
 *	The QUT Bioinformatics Collection is open source software released under the 
 *	Microsoft Public License (Ms-PL): http://www.microsoft.com/opensource/licenses.mspx.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;

namespace QUT.Bio.Util {
	public partial class PopupWindow : UserControl {
		//--------------------- Variables ------------------------------------------

		private Canvas container;
		public event WindowChangeEventHandler WindowChanged;
		private double left;
		private double top;
		private double width;
		private double height;
		private bool dragging;
		private Point mousePosition;
		private bool maximized = false;

		//--------------------- Constructor ----------------------------------------

		public PopupWindow() {
			InitializeComponent();
			SizeChanged += OnSizeChanged;
			titleBar.maximizeUpDown.Click += OnMaximizeUpDown;
			titleBar.close.Click += OnCloseClicked;
			MouseLeftButtonDown += OnMouseButtonDown;

			edgeElements = new Dictionary<Edge, Rectangle>{
				{ Edge.None, null },
				{ Edge.TopLeft, r0 },
				{ Edge.TopTopLeft, r1 },
				{ Edge.Top, r2 },
				{ Edge.TopTopRight, r3 },
				{ Edge.TopRight, r4 },
				{ Edge.Left, r5 },
				{ Edge.Right, r6 },
				{ Edge.BottomLeft, r7 },
				{ Edge.BottomBottomLeft, r8 },
				{ Edge.Bottom, r9 },
				{ Edge.BottomBottomRight, r10 },
				{ Edge.BottomRight, r11 }
			};
			
			foreach ( Rectangle r in edgeElements.Values ) {
				if ( r != null ) {
					r.MouseLeftButtonDown += SizeHandleButtonDown;
				}
			}
		}

		//--------------------- Properties -----------------------------------------

		public String Title {
			get {
				return titleBar.titleBlock.Text;
			}
			set {
				titleBar.titleBlock.Text = value;
			}
		}

		public BorderLayout ClientArea {
			get {
				return content;
			}
		}

		public Canvas Container {
			get {
				return container;
			}
			set {
				if ( value != container ) {
					if ( container != null ) {
						container.Children.Remove( this );
					}

					container = value;

					if ( container != null ) {
						container.Children.Add( this );
						EnsureFit();
						NotifyChange( WindowChangeEventType.WindowShown );
					}
					else {
						NotifyChange( WindowChangeEventType.WindowHidden );
					}
				}
			}
		}

		public double Left {
			get {
				return (double) GetValue( Canvas.LeftProperty );
			}
			set {
				SetValue( Canvas.LeftProperty, value );
			}
		}

		public double Top {
			get {
				return (double) GetValue( Canvas.TopProperty );
			}
			set {
				SetValue( Canvas.TopProperty, value );
			}
		}
		
		public Rectangle DropShadow {
			get {
				return dropShadow;
			}
		}
		
		public TranslateTransform DropShadowTranslation {
			get {
				return dropShadowTranslation;
			}
		}
		
		//--------------------- Methods --------------------------------------------

		void EnsureFit() {
			if ( container == null ) {
				return;
			}

			double left = (double) GetValue( Canvas.LeftProperty );
			double top = (double) GetValue( Canvas.TopProperty );

			if ( ActualWidth > container.ActualWidth ) {
				Width = container.ActualWidth;
			}

			if ( ActualHeight > container.ActualHeight ) {
				Height = container.ActualHeight;
			}
			
			if ( left >= container.ActualWidth - ActualWidth ) {
				left = container.ActualWidth - ActualWidth - 1;
			}

			if ( top >= container.ActualHeight - ActualHeight ) {
				top = container.ActualHeight - ActualHeight - 1;
			}

			if ( left < 0 ) {
				left = 0;
			}

			if ( top < 0 ) {
				top = 0;
			}

			SetValue( Canvas.LeftProperty, left );
			SetValue( Canvas.TopProperty, top );
		}

		void NotifyChange( WindowChangeEventType eventType ) {
			if ( WindowChanged != null ) {
				WindowChanged( this, eventType );
			}
		}

		void OnSizeChanged( object sender, SizeChangedEventArgs e ) {
			EnsureFit();
			NotifyChange( WindowChangeEventType.Resize );
		}

		void OnMaximizeUpDown( object sender, EventArgs e ) {
			switch ( (string) titleBar.maximizeUpDown.Content ) {
				case "O":
					left = (double) GetValue( Canvas.LeftProperty );
					top = (double) GetValue( Canvas.TopProperty );
					width = ActualWidth;
					height = ActualHeight;
					SetValue( Canvas.LeftProperty, 0.0 );
					SetValue( Canvas.TopProperty, 0.0 );
					Width = container.ActualWidth;
					Height = container.ActualHeight;
					titleBar.maximizeUpDown.Content = "o";
					maximized = true;
					NotifyChange( WindowChangeEventType.MaximizeUp );
					break;
				case "o":
					SetValue( Canvas.LeftProperty, left );
					SetValue( Canvas.TopProperty, top );
					Width = width;
					Height = height;
					titleBar.maximizeUpDown.Content = "O";
					maximized = false;
					NotifyChange( WindowChangeEventType.MaximizeDown );
					break;
				default:
					break;
			}
		}

		protected void OnCloseClicked( object sender, EventArgs e ) {
			Container = null;
			NotifyChange( WindowChangeEventType.WindowClosed );
		}

		void OnMouseButtonDown( object sender, MouseButtonEventArgs e ) {
			e.Handled = true;
			ToFront();
			dragging = true;
			mousePosition = e.GetPosition( null );
			CaptureMouse();
			MouseLeftButtonUp += OnMouseButtonUp;
			MouseMove += OnMouseMove;
		}

		void OnMouseButtonUp( object sender, MouseButtonEventArgs e ) {
			e.Handled = true;
			ToFront();
			dragging = false;
			ReleaseMouseCapture();
			MouseLeftButtonUp -= OnMouseButtonUp;
			MouseMove -= OnMouseMove;
		}

		void OnMouseMove( object sender, MouseEventArgs e ) {
			Point newMousePosition = e.GetPosition( null );

			if ( dragging ) {
				// move the origin of the display by the same amount as the mouse moved
				Left += newMousePosition.X - mousePosition.X;
				Top += newMousePosition.Y - mousePosition.Y;
				EnsureFit();
				mousePosition = newMousePosition;
			}
		}

		public void ToBack() {
			if ( container != null && container.Children.Count > 0 && container.Children[0] != this ) {
				container.Children.Remove( this );
				container.Children.Insert( 0, this );
			}
		}

		public void ToFront() {
			if ( container != null && container.Children.Count > 0 && container.Children[container.Children.Count - 1] != this ) {
				container.Children.Remove( this );
				container.Children.Add( this );
			}
		}

		private enum Edge {
			None,
			TopLeft,
			TopTopLeft,
			Top,
			TopTopRight,
			TopRight,
			Left,
			Right,
			BottomLeft,
			BottomBottomLeft,
			Bottom,
			BottomBottomRight,
			BottomRight
		}

		private Dictionary<Edge, Rectangle> edgeElements;

		private Dictionary<Edge, int> leftMod = new Dictionary<Edge, int> {
				{ Edge.None, 0 },
				{ Edge.TopLeft, 1 },
				{ Edge.TopTopLeft, 1 },
				{ Edge.Top, 0 },
				{ Edge.TopTopRight, 0 },
				{ Edge.TopRight, 0 },
				{ Edge.Left, 1 },
				{ Edge.Right, 0 },
				{ Edge.BottomLeft, 1 },
				{ Edge.BottomBottomLeft, 1 },
				{ Edge.Bottom, 0 },
				{ Edge.BottomBottomRight, 0 },
				{ Edge.BottomRight, 0 }
		};

		private Dictionary<Edge, int> topMod = new Dictionary<Edge, int> {
				{ Edge.None, 0 },
				{ Edge.TopLeft, 1 },
				{ Edge.TopTopLeft, 1 },
				{ Edge.Top, 1 },
				{ Edge.TopTopRight, 1 },
				{ Edge.TopRight, 1 },
				{ Edge.Left, 0 },
				{ Edge.Right, 0 },
				{ Edge.BottomLeft, 0 },
				{ Edge.BottomBottomLeft, 0 },
				{ Edge.Bottom, 0 },
				{ Edge.BottomBottomRight, 0 },
				{ Edge.BottomRight, 0 }
		};

		private Dictionary<Edge, int> widthMod = new Dictionary<Edge, int> {
				{ Edge.None, 0 },
				{ Edge.TopLeft, -1 },
				{ Edge.TopTopLeft, -1 },
				{ Edge.Top, 0 },
				{ Edge.TopTopRight, 1 },
				{ Edge.TopRight, 1 },
				{ Edge.Left, -1 },
				{ Edge.Right, 1 },
				{ Edge.BottomLeft, -1 },
				{ Edge.BottomBottomLeft, -1 },
				{ Edge.Bottom, 0 },
				{ Edge.BottomBottomRight, 1 },
				{ Edge.BottomRight, 1 }
		};

		private Dictionary<Edge, int> heightMod = new Dictionary<Edge, int> {
				{ Edge.None, 0 },
				{ Edge.TopLeft, -1 },
				{ Edge.TopTopLeft, -1 },
				{ Edge.Top, -1 },
				{ Edge.TopTopRight, -1 },
				{ Edge.TopRight, -1 },
				{ Edge.Left, 0 },
				{ Edge.Right, 0 },
				{ Edge.BottomLeft, 1 },
				{ Edge.BottomBottomLeft, 1 },
				{ Edge.Bottom, 1 },
				{ Edge.BottomBottomRight, 1 },
				{ Edge.BottomRight, 1 }
		};

		private Rectangle sizeElement;
		private Edge sizeEdge;
		private Point sizeMouseLocation;

		private void SizeHandleButtonDown( object sender, MouseButtonEventArgs args ) {
			args.Handled = true;
			
			if ( maximized ) {
				return;
			}
			
			sizeElement = (Rectangle) sender;
			sizeEdge = ( from r in edgeElements
						 where r.Value == sizeElement
						 select r.Key ).First();
			sizeElement.CaptureMouse();
			sizeMouseLocation = args.GetPosition(null);
			sizeElement.MouseMove += SizeHandleMouseMove;
			sizeElement.MouseLeftButtonUp += SizeHandleButtonUp;
		}

		private void SizeHandleButtonUp( object sender, MouseButtonEventArgs args ) {
			sizeElement.ReleaseMouseCapture();
			sizeElement.MouseMove -= SizeHandleMouseMove;
			sizeElement.MouseLeftButtonUp -= SizeHandleButtonUp;
		}

		private void SizeHandleMouseMove( object sender, MouseEventArgs args ) {

			Point p = args.GetPosition(null);
			double dx = p.X - sizeMouseLocation.X;
			double dy = p.Y - sizeMouseLocation.Y;
			sizeMouseLocation = p;
			
			Left += leftMod[sizeEdge] * dx;
			Top += topMod[sizeEdge] * dy;
			Width = Math.Max( MinWidth, Width + widthMod[sizeEdge] * dx );
			Height = Math.Max( MinHeight, Height + heightMod[sizeEdge] * dy );
			EnsureFit();
		}
	}
}
